package mmc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

import mmc.networking.Message;
import mmc.networking.Quit;
import mmc.networking.Quit.QuitCause;
import mmc.util.APSCounter;
import mmc.util.ThroughputMeterDebugInputStream;
import mmc.util.ThroughputMeterDebugOutputStream;

public class RemoteServer implements Runnable{
	public final Socket socket;
	private Client client;
	private boolean quit = false;
	private ObjectInputStream in;
	private ObjectOutputStream out;
	private APSCounter packetsOut = new APSCounter("Us");
	private APSCounter byteUpCounter = new APSCounter("bps up");
	private APSCounter byteDownCounter = new APSCounter("bps down");
	
	public RemoteServer(Client client, Socket socket) throws IOException{
		this.client = client;
		this.socket = socket;
		System.out.println("connected to server : "+socket);
		in = new ObjectInputStream(new ThroughputMeterDebugInputStream(byteDownCounter,socket.getInputStream()));
		out = new ObjectOutputStream(new ThroughputMeterDebugOutputStream(byteUpCounter,socket.getOutputStream()));
		
		new Thread(this).start();
	}
	
	public RemoteServer(Client client, String host, int port) throws IOException{
		this(client, new Socket(host, port));
	}
	
	@Override
	public void run() {
		try{
			while(!quit){
				((Message) in.readObject()).executeOnClient(this);
			}
			
			out.close();
			in.close();
			socket.close();
		}catch(Exception e){
			if(!client.isQuitting()){
				System.out.println("Exception in "+getClass().getCanonicalName()+"'s mainloop:");
				e.printStackTrace();
			}
		}finally{
			try {
				socket.close();
			} catch (IOException e) {e.printStackTrace();}
		}
	}
	
	Object sendLock = new Object();
	public void post(Message m){
		synchronized(sendLock){
			try {
				packetsOut.notify(0,1);
				out.writeUnshared(m);
			} catch(SocketException e) {
				if(!client.isQuitting()){
					e.printStackTrace();
					client.setQuitCause(Quit.QuitCause.CONNECTION_BROKEN);
				}
				client.closeRequested = true;
			}catch (IOException e) {
				System.out.println("Error while sending msg to "+socket);
				e.printStackTrace();
			}
		}
	}
	
	public void closeSocket(Quit.QuitCause quitCause){
		quit = true;
		
		client.setQuitCause(quitCause);
		
		System.out.println("request socket close");
	}
	
	public void quit(){
		QuitCause qc = Quit.QuitCause.CLIENT_SHUTDOWN;
		post(new Quit(qc));
		
		client.setQuitCause(qc);
	}
	
	public Client getClient(){
		return client;
	}

	public APSCounter getPacketsOut() {
		return packetsOut;
	}

	public APSCounter getByteUpCounter() {
		return byteUpCounter;
	}

	public APSCounter getByteDownCounter() {
		return byteDownCounter;
	}
}
